package yolorpc.proxy;


import cn.hutool.core.collection.CollUtil;
import lombok.extern.slf4j.Slf4j;
import yolorpc.RpcApplication;
import yolorpc.config.RpcConfig;
import yolorpc.constant.RpcConstant;
import yolorpc.fault.retry.RetryStrategy;
import yolorpc.fault.retry.RetryStrategyFactory;
import yolorpc.fault.tolerant.TolerantStrategy;
import yolorpc.fault.tolerant.TolerantStrategyFactory;
import yolorpc.fault.tolerant.TolerantStrategyKeys;
import yolorpc.loadbalancer.LoadBalancer;
import yolorpc.loadbalancer.LoadBalancerFactory;
import yolorpc.model.RpcRequest;
import yolorpc.model.RpcResponse;
import yolorpc.model.ServiceMetaInfo;
import yolorpc.registry.Registry;
import yolorpc.registry.RegistryFactory;
import yolorpc.serializer.Serializer;
import yolorpc.serializer.SerializerFactory;
import yolorpc.server.tcp.VertxTcpClient;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author yolo
 * @Program RPC
 * @description 服务代理（JDK动态代理）
 * @create 2025-04-21 18:07
 **/
@Slf4j
public class ServiceProxy implements InvocationHandler {

    /**
     * @param proxy
     * @param method
     * @param args
     * @return Object
     * @descript 调用代理
     * @author yolo
     * @date 2025/4/21 18:08
     **/
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 指定序列化器
        final Serializer serializer = SerializerFactory.getInstance(RpcApplication.getRpcConfig().getSerializer());

        log.info("======代理开始======");

        log.info("======开始构造请求======");
        // 构造请求
        String serviceName = method.getDeclaringClass().getName();
        RpcRequest rpcRequest = RpcRequest.builder()
                .serviceName(serviceName)
                .methodName(method.getName())
                .parameterTypes(method.getParameterTypes())
                .args(args)
                .build();
        log.info("======构造请求完成======");
        try {
            log.info("======从注册中心获取服务地址======");
            // 从注册中心获取服务提供者请求地址
            RpcConfig rpcConfig = RpcApplication.getRpcConfig();
            Registry registry = RegistryFactory.getInstance(rpcConfig.getRegistryConfig().getRegistry());
            ServiceMetaInfo serviceMetaInfo = new ServiceMetaInfo();
            serviceMetaInfo.setServiceName(serviceName);
            serviceMetaInfo.setServiceVersion(RpcConstant.DEFAULT_SERVICE_VERSION);
            List<ServiceMetaInfo> serviceMetaInfoList = registry.serviceDiscovery(serviceMetaInfo.getServiceKey());
            if (CollUtil.isEmpty(serviceMetaInfoList)) {
                throw new RuntimeException("暂无服务地址");
            }
            log.info("======服务地址获取完成======");
            log.info("======开始获取负载均衡配置======");
            // 负载均衡
            LoadBalancer loadBalancer = LoadBalancerFactory.getInstance(rpcConfig.getLoadBalancer());
            // 将调用方法名（请求路径）作为负载均衡参数
            Map<String, Object> requestParams = new HashMap<>();
            requestParams.put("methodName", rpcRequest.getMethodName());
            ServiceMetaInfo selectedServiceMetaInfo = loadBalancer.select(requestParams, serviceMetaInfoList);
            log.info("======负载均衡配置为：{}======", rpcConfig.getLoadBalancer());
            log.info("======获取负载均衡配置完成======");
            // rpc 请求
            // 使用重试机制
            log.info("======开始获取重试机制配置======");
            RetryStrategy retryStrategy = RetryStrategyFactory.getInstance(rpcConfig.getRetryStrategy());
            log.info("======负载均衡配置为：{}======", rpcConfig.getRetryStrategy());
            log.info("======获取重试机制配置完成======");
            log.info("======开始调用方法======");
            RpcResponse rpcResponse = null;
            try {
                rpcResponse = retryStrategy.doRetry(() ->
                        VertxTcpClient.doRequest(rpcRequest, selectedServiceMetaInfo)
                );
            } catch (Exception e) {
                // 容错机制
                log.info("======触发容错======");
                TolerantStrategy tolerantStrategy = TolerantStrategyFactory.getInstance(rpcConfig.getTolerantStrategy());
                log.info("======容错处理类为：{}======", tolerantStrategy.getClass().getName());
                // 判断容错处理类型
                Map<String, Object> context = new HashMap<>();
                context.put("rpcRequest", rpcRequest);
                context.put("exception", e);
                context.put("serviceMetaInfoList", serviceMetaInfoList);
                rpcResponse = tolerantStrategy.doTolerant(context, e);
            }
            log.info("======远程调用结束======");
            return rpcResponse.getData();
        } catch (Exception e) {
            throw new RuntimeException("调用失败");
        }
    }
}
